package com.tarena.lbs.coupon.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tarena.lbs.activity.api.ActivityApi;
import com.tarena.lbs.activity.api.pojo.ActivityDto;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.model.Result;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.common.principle.UserPrinciple;
import com.tarena.lbs.common.utils.AuthenticationContextUtils;
import com.tarena.lbs.coupon.api.pojo.param.UserCouponsParam;
import com.tarena.lbs.coupon.config.CustomIntegerConverter;
import com.tarena.lbs.coupon.dao.mapper.CouponCodeMapper;
import com.tarena.lbs.coupon.dao.mapper.CouponMapper;
import com.tarena.lbs.coupon.dao.mapper.StockMapper;
import com.tarena.lbs.coupon.dao.mapper.UserCouponsMapper;
import com.tarena.lbs.coupon.dao.repository.CouponESRepository;
import com.tarena.lbs.coupon.dao.repository.CouponRepository;
import com.tarena.lbs.coupon.enums.StockResultEnum;
import com.tarena.lbs.coupon.pojo.param.CouponParam;
import com.tarena.lbs.coupon.pojo.po.CouponCodePO;
import com.tarena.lbs.coupon.pojo.po.CouponPO;
import com.tarena.lbs.coupon.pojo.po.CouponStockPO;
import com.tarena.lbs.coupon.pojo.po.UserCouponsPO;
import com.tarena.lbs.coupon.pojo.query.CouponQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponQuery;
import com.tarena.lbs.coupon.pojo.vo.CouponVO;
import com.tarena.lbs.coupon.pojo.vo.UserCouponsVO;
import com.tarena.lbs.coupon.service.CouponService;
import com.tarena.lbs.user.api.UserApi;
import com.tarena.lbs.user.api.pojo.dto.AdminDto;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CouponServiceImpl implements CouponService {
    @Autowired
    private CouponRepository couponRepository;
    @DubboReference
    private ActivityApi activityApi;
    @DubboReference
    private UserApi userApi;
    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private CouponCodeMapper couponCodeMapper;
    @Autowired
    private UserCouponsMapper userCouponsMapper;
    @Autowired
    private StockMapper stockMapper;
    @Autowired
    private CouponESRepository couponESRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public Result userInsertCoupon(UserCouponsParam param) throws BusinessException {
        //T1 T2 T3 T4 T5
        //T1 1 查询充足 足
        //T2 1 查询充足 足
        //T1 2 写数据
        //T2 2 写数据
        //T1 3 减库存(-1) 剩余0
        //T2 3 也减库存(-1) 剩余-1
        //1.读取当前优惠券库存数量 判断是否充足 每次只需要领取一张
        CouponPO couponPO =couponMapper.selectById(param.getCouponId());
        Integer userId=getUserIdFromHeader();
        ActivityDto activity = activityApi.getActivityById(param.getActivityId());
        Boolean tryLock=false;
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        String lockKey = "receive:coupon:" + couponPO.getId();
        String code = UUID.randomUUID().toString();
        //第一次进来抢锁 不需要等待 第二次重抢 第三次重抢需要等待1秒钟
        int count=0;
        do {
            //给线程一个重抢上3次 重抢抢不到就别抢了
            Asserts.isTrue(count>3,new BusinessException("-2","优惠券领取失败"));
            if (count>0){
                //当前线程 已经不是第一次抢锁了 等1秒
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    log.error("线程等待异常",e);
                }
            }
            Boolean enough = stockIsEnough(couponPO.getId());
            //如果库存已经没有了 ==0 方法到这就结束了
            Asserts.isTrue(!enough, new BusinessException("-2", "优惠券库存不足"));
            //1.抢锁 有key value 操作redis setnx是string类型
            //SETNX key value EX 5 原子级
            log.info("库存充足开始抢锁,key:{},value:{}",lockKey,code);
            tryLock = opsForValue.setIfAbsent(lockKey, code, 5, TimeUnit.SECONDS);
            count++;
        }while (!tryLock);
        //释放锁一定要在finally
        try {
            log.info("抢锁成功,开始领取优惠券");
            //2.创建一个可领取的优惠券码 返回券码code编码
            String couponCode = createCouponCode(couponPO);
            //3.记录一下领取信息
            createUserReceiveCouponInfo(param, userId, activity, couponPO, couponCode);
            //4.扣减库存 扣减那个优惠券库存
            reduceStock(couponPO.getId());
            return Result.success();
        }catch (Exception e){
            throw e;
        }finally {
            log.info("释放锁");
            //封装使用lua脚本
            String value = opsForValue.get(lockKey);
            if (value!=null&&value.equals(code)){
                log.info("锁标识正确 可以释放");
                redisTemplate.delete(lockKey);
            }
        }
    }

    private void reduceStock(Integer id) {
        Integer num=stockMapper.selectByCouponId( id);
        num--;
        stockMapper.updateStock(num);
    }

    private void createUserReceiveCouponInfo(UserCouponsParam param, Integer userId, ActivityDto activity, CouponPO couponPO, String couponCode) {
        UserCouponsPO userCouponsPO=new UserCouponsPO();
        userCouponsPO.setUserId(userId);
        userCouponsPO.setCouponId(param.getCouponId());
        userCouponsPO.setCouponType(couponPO.getCouponType());
        userCouponsPO.setCouponValue(couponPO.getDiscountValue());
        userCouponsPO.setCreateAt(new Date());
        userCouponsPO.setUpdateAt(new Date());
        userCouponsPO.setActivityId(activity.getId());
        userCouponsPO.setCouponCode(couponCode);
        userCouponsMapper.insert(userCouponsPO);
    }

    private String createCouponCode(CouponPO couponPO) {
        //1.生成一个随机的优惠券码
        String couponCode = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10);
        //2.检查优惠券码是否已存在
        CouponCodePO couponCodePO = couponCodeMapper.selectOne(new LambdaQueryWrapper<CouponCodePO>()
                .eq(CouponCodePO::getCouponCode, couponCode));
        if (couponCodePO != null) {
            // 递归生成新的优惠券码
            return createCouponCode(couponPO);
        }
        //3.创建优惠券码记录
        couponCodePO = new CouponCodePO();
        couponCodePO.setCouponCode(couponCode);
        couponCodePO.setCouponId(couponPO.getId());
        couponCodeMapper.insert(couponCodePO);
        return couponCode;
    }

    private Boolean stockIsEnough(Integer id) {
        Integer num=couponMapper.selectStockById(id);
        if (num>0){
            return true;
        }else{
            return false;
        }
    }

    @Override
    public PageResult<CouponVO> selectCoupon(CouponQuery query) throws BusinessException {
        // 创建MyBatis Plus的分页对象
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<CouponPO> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
                        query.getPageNo(),
                        query.getPageSize()
                );

        // 生成PO的查询条件
        QueryWrapper<CouponPO> queryWrapper = generatePOQuery(query);
        log.debug("业务层前端传来pageNo:{}和PageSize:{}", query.getPageNo(), query.getPageSize());
        log.debug("业务层queryWrapper:{}", queryWrapper);

        // 执行分页查询
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<CouponPO> resultPage =
                couponMapper.selectPage(page, queryWrapper);

        // 转换结果
        List<CouponVO> couponVOS = convertPoListToVoList(resultPage.getRecords());

        log.debug("业务层List couponVOS:{}", couponVOS);

        // 构建返回结果
        PageResult<CouponVO> pageResult = new PageResult<>();
        pageResult.setObjects(couponVOS);
        pageResult.setTotal(resultPage.getTotal());  // 使用实际查询的总数
        pageResult.setPageNo(query.getPageNo());
        pageResult.setPageSize(query.getPageSize());

        log.debug("业务层pageResult:{}", pageResult);
        return pageResult;
    }

    @Override
    public StockResultEnum addCoupon(CouponParam couponParam) {
        log.info("新增优惠券：{}", couponParam);
        CouponPO couponPO = new CouponPO();
        BeanUtils.copyProperties(couponParam, couponPO);
        try {
            couponPO.setStartDate(DATE_FORMAT.parse(couponParam.getStartDate()));
            couponPO.setEndDate(DATE_FORMAT.parse(couponParam.getEndDate()));
        } catch (ParseException e) {
            log.error("日期格式错误", e);
            return StockResultEnum.DATE_ERROR;
        }

        couponPO.setCreateAt(new Date());
        couponPO.setUpdateAt(new Date());
        couponPO.setEnableStatus(1);
        Integer userId=getUserIdFromHeader();
        log.debug("coupon业务层userId",userId);
        AdminDto dto =userApi.getAdminById(userId);
        //当前商家的id
        Integer businessId=dto.getBusinessId();
        log.debug("当前登陆人商家id:{}",businessId);
        log.debug("当前登录人id:{},信息:{}",userId,dto);
        couponPO.setBusinessId(businessId);
        couponPO.setStatus(1);
        int rows = couponMapper.insert(couponPO);
        if (rows != 1) {
            log.error("新增优惠券失败");
            return StockResultEnum.FAILED;
        }
        return StockResultEnum.SUCCESS;
    }

    @Override
    public CouponVO detail(Integer id) {
        log.info("查询优惠券详情，id:{}", id);
        if (id == null) {
            log.error("查询优惠券详情失败，id为空");
        }
        CouponPO couponPO = couponMapper.selectById(id);
        if (couponPO == null) {
            log.error("查询优惠券详情失败，id:{} 不存在", id);
        }
        CouponVO couponVO = new CouponVO();
        BeanUtils.copyProperties(couponPO, couponVO);
        log.info("查询优惠券详情成功，id:{}，结果:{}", id, couponVO);
        return couponVO;
    }


    @Override
    public PageResult<UserCouponsVO> selectByCountId(UserCouponQuery couponQuery) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<UserCouponsPO> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
                        couponQuery.getPageNo(),
                        couponQuery.getPageSize()
                );
            log.debug("业务层用户coupon page:{}", page);
        QueryWrapper<UserCouponsPO> queryWrapper = generateUserPOQuery(couponQuery);
        log.debug("业务层前端传来pageNo:{}和PageSize:{}", couponQuery.getPageNo(), couponQuery.getPageSize());
        log.debug("业务层queryWrapper:{}", queryWrapper);
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<UserCouponsPO> resultPage =
                userCouponsMapper.selectPage(page, queryWrapper);
        log.debug("业务层resultPage:{}",resultPage);
        // 转换结果
        List<UserCouponsVO> userCouponsVOS = convertUCPoListToVoList(resultPage.getRecords());
        for (UserCouponsVO vo : userCouponsVOS) {
            System.out.println("遍历UserCouponsVO:{}"+ JSON.toJSONString(vo));
            log.debug("业务层List useCouponsVO:{}", vo);
        }
        PageResult<UserCouponsVO> pageResult = new PageResult<>();
        pageResult.setObjects(userCouponsVOS);
        pageResult.setTotal(resultPage.getTotal());  // 使用实际查询的总数
        pageResult.setPageNo(couponQuery.getPageNo());
        pageResult.setPageSize(couponQuery.getPageSize());

        log.debug("业务层pageResult:{}", pageResult);
        return pageResult;
    }

    @Override
    public Integer addCouponExcel(MultipartFile file) throws IOException {
        List<CouponPO> excels = EasyExcel.read(file.getInputStream())
                .head(CouponVO.class)
                .registerConverter(new CustomIntegerConverter()) // 在这里注册转换器
                .sheet()
                .doReadSync();
        log.info("Excel:{}",excels.toString());
        return couponESRepository.addUserBatch(excels);
    }

    @Override
    public List<CouponVO> getCouponList() {
        List<CouponVO> couponVOS = couponESRepository.selectCouponPOList();
        log.info("Excel:{}",couponVOS.toString());
        return couponVOS;
    }

    @Override
    public int updateCoupon(CouponParam couponsParam) {
        CouponPO couponPO=new CouponPO();
        couponPO.setUpdateAt(new Date());
        BeanUtils.copyProperties(couponsParam,couponPO);
        Integer rows=couponMapper.updateById(couponPO);
        log.debug("更新coupon返回行数:{}",rows);
        return rows;
    }

    @Override
    public int deleteCoupon(Integer id) {
        Integer rows=couponMapper.deleteById(id);
        return rows;
    }

    private List<UserCouponsVO> convertUCPoListToVoList(List<UserCouponsPO> records) {
        if (records == null) {
            return new ArrayList<>();
        }
        return records.stream()
                .map(po -> {
                    UserCouponsVO vo = new UserCouponsVO();
                    BeanUtils.copyProperties(po, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }

    private QueryWrapper<UserCouponsPO> generateUserPOQuery(UserCouponQuery couponQuery) {
        QueryWrapper<UserCouponsPO> queryWrapper = new QueryWrapper<>();
        Integer userId=getUserIdFromHeader();
        log.debug("coupon业务层userId:{}",userId);
        if (userId!=null){
            queryWrapper.eq("user_id",userId);
        }
        if (couponQuery != null) {
            // 修正字段名：coupon_tyep -> coupon_type
            if (couponQuery.getCouponType() != null) {
                queryWrapper.eq("coupon_type", couponQuery.getCouponType());
            }
            // 字符串模糊匹配
            if (StringUtils.isNotBlank(couponQuery.getOrderNo())) {
                queryWrapper.like("order_no", couponQuery.getOrderNo());
            }
            // 状态查询
            if (couponQuery.getStatus() != null) {
                queryWrapper.eq("status", couponQuery.getStatus());
                log.debug("优惠券状态:{}",couponQuery.getStatus());
            }
            if (couponQuery.getReceiveChannel()!=null){
                queryWrapper.eq("receive_channel", couponQuery.getReceiveChannel());
                log.debug("优惠券渠道:{}",couponQuery.getReceiveChannel());
            }
        }
        return queryWrapper;
    }


    private QueryWrapper<CouponPO> generatePOQuery(CouponQuery query) throws BusinessException {
        QueryWrapper<CouponPO> queryWrapper = new QueryWrapper<>();
        //当前用户id
        Integer userId = getUserIdFromHeader();
        log.debug("登录人userId:{}",userId);
        AdminDto dto =userApi.getAdminById(userId);
        //当前商家的id
        Integer businessId=dto.getBusinessId();
        log.debug("当前登陆人商家id:{}",businessId);
        log.debug("当前登录人id:{},信息:{}",userId,dto);
        queryWrapper.eq("business_id",businessId);
        if (query != null) {
            // 修正字段名：coupon_tyep -> coupon_type
            if (query.getCouponType() != null) {
                queryWrapper.eq("coupon_type", query.getCouponType());
            }
            // 字符串模糊匹配
            if (StringUtils.isNotBlank(query.getCouponName())) {
                queryWrapper.like("coupon_name", query.getCouponName());
            }
            // 状态查询
            if (query.getStatus() != null) {
                queryWrapper.eq("status", query.getStatus());
                log.debug("优惠券状态:{}",query.getStatus());
            }
            log.debug("业务层商家businessid:{]",businessId);
        }
        return queryWrapper;
    }
    // 检查请求头中是否确实包含该字段
    private Integer getUserIdFromHeader() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) return null;
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("authorization");
            log.debug("tokenn:{}",token);
            if (StringUtils.isNotBlank(token)) {
                Integer number=parseUserIdFromToken(token);
                log.debug("用户number:{}",number);
                return number;
            }
            return null;
        } catch (Exception e) {
            log.error("从请求头获取用户ID失败", e);
            return null;
        }
    }
    private Integer parseUserIdFromToken(String token) {
        try {
            // JWT token 由三部分组成，用点分隔：Header.Payload.Signature
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                log.warn("JWT token 格式不正确");
                return null;
            }
            // 解码 Payload 部分 (Base64Url)
            String payload = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);
            // 解析 JSON payload
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode payloadNode = objectMapper.readTree(payload);
            // 根据你的 JWT 结构获取用户ID
            // 从你的 token 看，payload 包含一个 payload 字段，里面是用户信息
            if (payloadNode.has("payload")) {
                String innerPayload = payloadNode.get("payload").asText();
                JsonNode userInfo = objectMapper.readTree(innerPayload);

                if (userInfo.has("id")) {
                    return userInfo.get("id").asInt();
                }
            }
            // 或者直接查找 id 字段
            if (payloadNode.has("id")) {
                return payloadNode.get("id").asInt();
            }
            log.warn("在 JWT token 中未找到用户ID");
            return null;
        } catch (Exception e) {
            log.error("解析 JWT token 失败", e);
            return null;
        }
    }
    // PO转VO的转换方法
    private List<CouponVO> convertPoListToVoList(List<CouponPO> poList) {
        if (poList == null) {
            return new ArrayList<>();
        }
        return poList.stream()
                .map(po -> {
                    CouponVO vo = new CouponVO();
                    BeanUtils.copyProperties(po, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }
}
